mod calendar;

pub use 干支::*;
pub use 日::*;
pub use 月::*;
pub use 月份::*;

use None as 無;
use Option as 可選;
use Some as 有;

use crate::{Date, Year, 參照日, 非法};
use calendar::{某年春節日序, 某年某月日數, 某年閏月};

pub type 非法干支 = 非法<干支>;
pub type 非法月 = 非法<月>;
pub type 非法日 = 非法<日>;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct 年 {
    pub(super) 年: u16,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum 月 {
    平(月份),
    閏(月份),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct 年月日 {
    pub 年: 年,
    pub 月: 月,
    pub 日: 日,
}

def_enum! {
    pub enum 干支(u8) {
        甲子 =  0, 乙丑 =  1, 丙寅 =  2, 丁卯 =  3, 戊辰 =  4, 己巳 =  5, 庚午 =  6, 辛未 =  7, 壬申 =  8, 癸酉 =  9,
        甲戌 = 10, 乙亥 = 11, 丙子 = 12, 丁丑 = 13, 戊寅 = 14, 己卯 = 15, 庚辰 = 16, 辛巳 = 17, 壬午 = 18, 癸未 = 19,
        甲申 = 20, 乙酉 = 21, 丙戌 = 22, 丁亥 = 23, 戊子 = 24, 己丑 = 25, 庚寅 = 26, 辛卯 = 27, 壬辰 = 28, 癸巳 = 29,
        甲午 = 30, 乙未 = 31, 丙申 = 32, 丁酉 = 33, 戊戌 = 34, 己亥 = 35, 庚子 = 36, 辛丑 = 37, 壬寅 = 38, 癸卯 = 39,
        甲辰 = 40, 乙巳 = 41, 丙午 = 42, 丁未 = 43, 戊申 = 44, 己酉 = 45, 庚戌 = 46, 辛亥 = 47, 壬子 = 48, 癸丑 = 49,
        甲寅 = 50, 乙卯 = 51, 丙辰 = 52, 丁巳 = 53, 戊午 = 54, 己未 = 55, 庚申 = 56, 辛酉 = 57, 壬戌 = 58, 癸亥 = 59,
    }
}

def_enum! {
    pub enum 月份(u8) {
        正月 = 1, 二月 = 2, 三月 = 3, 四月 = 4, 五月 = 5, 六月 = 6,
        七月 = 7, 八月 = 8, 九月 = 9, 十月 = 10, 冬月 = 11, 臘月 = 12 | 0,
    }
}

def_enum! {
    pub enum 日(u8) {
        初一 =  1, 初二 =  2, 初三 =  3, 初四 =  4, 初五 =  5, 初六 =  6, 初七 =  7, 初八 =  8, 初九 =  9, 初十 = 10,
        十一 = 11, 十二 = 12, 十三 = 13, 十四 = 14, 十五 = 15, 十六 = 16, 十七 = 17, 十八 = 18, 十九 = 19, 二十 = 20,
        廿一 = 21, 廿二 = 22, 廿三 = 23, 廿四 = 24, 廿五 = 25, 廿六 = 26, 廿七 = 27, 廿八 = 28, 廿九 = 29, 三十 = 30,
    }
}

impl_display!(年 = |self, f| write!(f, "{}年", self.干支()));
impl_display!(干支 = Debug);
impl_display!(月份 = Debug);
impl_display!(日 = Debug);
impl_display!(年月日 = |self, f| write!(f, "{}{}{}", self.年, self.月, self.日));
impl_display!(
    月 = |self, f| match self {
        平(月份) => 月份.fmt(f),
        閏(月份) => write!(f, "閏{}", 月份),
    }
);

impl 年 {
    pub fn 春節(self) -> 年月日 {
        self.正月初一()
    }
    pub fn 正月初一(self) -> 年月日 {
        年月日 {
            年: self,
            月: 正月.into(),
            日: 初一,
        }
    }

    pub fn 干支(self) -> 干支 {
        庚戌 + (self.年 - 參照日.年().年)
    }

    pub fn 閏月(self) -> 可選<月份> {
        某年閏月(self.年)
    }
}

#[derive(Clone, Copy)]
pub(super) struct 月序 {
    pub(super) 年: 年,
    閏月: 可選<月份>,
    pub(super) 月序: u8,
}

impl 月序 {
    fn 正月(年: 年) -> 月序 {
        月序::自年月(年, 正月.into())
    }
    fn 自年月(年: 年, 月: 月) -> 月序 {
        let 月序 = match (月, 年.閏月()) {
            (閏(月份), 有(閏月份)) if 月份 == 閏月份 => 月份.repr() + 1,
            (平(月份), 有(閏月份)) => 月份.repr() + (月份 > 閏月份) as u8,
            (平(月份), 無) => 月份.repr(),
            (閏(月份), 無) => {
                panic!("閏月不正確, 該年無閏月, 實際閏{}", 月份)
            }
            (閏(月份), 有(閏月份)) => {
                panic!("閏月不正確, 該年閏{}, 實際閏{}", 閏月份, 月份)
            }
        };
        月序 {
            年,
            閏月: 年.閏月(),
            月序,
        }
    }
    fn 日數(self) -> u8 {
        某年某月日數(self.年.年, self.月序)
    }
    fn 下月(self) -> Option<月序> {
        if self.月序 == 12 + self.閏月.is_some() as u8 {
            return None;
        }
        Some(月序 {
            年: self.年,
            閏月: self.閏月,
            月序: self.月序 + 1,
        })
    }
}

impl 月 {
    pub fn 月份(self) -> 月份 {
        match self {
            平(月份) => 月份,
            閏(月份) => 月份,
        }
    }
    pub(super) fn 月序(self, 年: 年) -> 月序 {
        月序::自年月(年, self)
    }
    pub fn 是閏月(self) -> bool {
        matches!(self, 閏(_))
    }
    pub fn 非閏月(self) -> bool {
        !self.是閏月()
    }
    pub fn 日數(self, 年: 年) -> u8 {
        self.月序(年).日數()
    }
}

impl 年月日 {
    pub fn 干支(self) -> 干支 {
        self.年.干支()
    }
    /// 從1開始
    pub fn 日序(self) -> u16 {
        let mut 日序 = self.日.repr() as u16;

        let 月序 = self.月.月序(self.年);

        let mut 整月 = 月序::正月(self.年);
        while 整月.月序 < 月序.月序 {
            日序 += u16::from(整月.日數());
            let Some(下月) = 整月.下月() else { break };
            整月 = 下月;
        }

        日序
    }
    pub fn to_date(self) -> Date {
        let days = self.日序() - 1 + (某年春節日序(self.年.年) as u16);
        Year::from(self.年).first_day().after_days(days.into())
    }
}

impl From<Year> for 年 {
    fn from(year: Year) -> Self {
        年 { 年: year.0 }
    }
}

impl From<月份> for 月 {
    fn from(月份: 月份) -> Self {
        平(月份)
    }
}

impl From<月> for 月份 {
    fn from(月: 月) -> Self {
        月.月份()
    }
}

impl_add_sub_modulo!(干支, 60, (u8, i8), (u16, i16), (u32, i32));

#[cfg(test)]
mod 測試 {
    use super::*;

    #[track_caller]
    fn 春節測試(year: u16, month: u8, day: u8) {
        let date = Date::new(year, month, day);
        assert_eq!(年::from(date.year).正月初一().to_date(), date);
    }

    #[test]
    fn 測試春節() {
        春節測試(2012, 1, 23);
        春節測試(2013, 2, 10);
        春節測試(2014, 1, 31);
        春節測試(2015, 2, 19);
        春節測試(2016, 2, 8);
        春節測試(2017, 1, 28);
        春節測試(2018, 2, 16);
        春節測試(2019, 2, 5);
        春節測試(2020, 1, 25);
        春節測試(2021, 2, 12);
        春節測試(2022, 2, 1);
        春節測試(2023, 1, 22);
        春節測試(2024, 2, 10);
    }
}
